package QueryHandler;

import java.util.Vector;

import DB.DisjointProjection;
import DB.IndependetProjection;
import DB.Join;
import DB.Selection;
import DB.Table;

/*
 * QueryEvaluator is our recursive implementation for the algorithm  for
 * supporting queries for which a safe plan exists (by using an algorithms 
 * studied in class. In case of approximation queries a message is sent to 
 * the user.
 */

public class QueryEvaluator {


	public static Table findSafePlan(DLQuery dlq) throws Exception{
		Vector<Relation> relations = new Vector<Relation>();
		relations = dlq.getRelations();		
		// the stop condition:
		if (relations.size() == 1 && dlq.getFreeVar().isEmpty()){
			Vector<Predicate> preds = new Vector<Predicate>();
			for (int i = 0; i < relations.size(); i++){
				preds = relations.elementAt(i).findAllPreds();
			}
			// compute by selection
			return new Selection(relations.elementAt(0).getTable(), preds).compute();			
		}
		else {
			// check if the first rule of the algorithm we 
			// in class is satisfied 
			String var = firstRule(dlq, relations);
			if (var!=null){
				RelationVar relVar = new RelationVar(var);
				Vector<String> projFields = relationVarToString(dlq.getHeadVars());
				DLQuery newDlq = new DLQuery(dlq, relVar);
				//compute by IndependedProjection
				return new IndependetProjection(findSafePlan(newDlq), projFields).compute();
			}		
			else { 
				// check if the second rule of the algorithm we 
				// in class is satisfied
				var = secondRule(dlq, relations);
				if (var != null){			
					RelationVar relVar = new RelationVar(var);
					Vector<String> projFields = relationVarToString(dlq.getHeadVars());
					DLQuery newDlq = new DLQuery(dlq, relVar);
					//compute by DisjointProjection
					return new DisjointProjection(findSafePlan(newDlq), projFields).compute();					
				}
				else{
					// check if possible to partitioned query to two 
					// different queries such that do not share any free variables
					if (relations.size() > 1){
						Vector<Relation> vec1 = new Vector<Relation>();
						vec1.add(relations.elementAt(0));
						DLQuery dlq1 = new DLQuery(getNewHead(dlq, relations.elementAt(0)), vec1);
						Vector<Relation> vec2 = new Vector<Relation>();
						vec2.add(relations.elementAt(1));
						DLQuery dlq2 = new DLQuery(getNewHead(dlq, relations.elementAt(1)), vec2);
						//compute by Join
						return new Join(findSafePlan(dlq1), findSafePlan(dlq2)).compute();
					}

				}
			}
		}
		throw new Exception("the query is #P-Complete");		
	}

/*
 * While computing the algorithm adds vars to the head of the query
 */
	
	private static Vector<RelationVar> getNewHead(DLQuery dlq, Relation relation) {
		Vector<RelationVar> ans = new Vector<RelationVar>();
		for(RelationVar rv : dlq.getHeadVars()){
			if (strContains(relation.getVars(), rv))
				ans.add(rv);
		}
		return ans;
	}

/*
 * Continuation to the above function - 
 * checks if the vars already exits in the head
 */
	
	private static boolean strContains(Vector<RelationVar> vars, RelationVar rv) {
		for(RelationVar r : vars)
			if (r.getName().equalsIgnoreCase(rv.getName()))
				return true;
		return false;
	}

/*
 * Replaces our RealtionVar type to the famous String class in java
 * by our "getName" function, which returns the RealtionVar's name
 */ 
	public static Vector<String> relationVarToString( Vector<RelationVar> vector){
		Vector<String> ans = new Vector<String>();
		for (int i = 0; i  < vector.size(); i++)
			ans.add(vector.elementAt(i).getName());
		return ans;

	}

/*
 * The first rule of the find safe plan algorithm	
 */
	
	private static String firstRule(DLQuery dlq, Vector<Relation> relations)  {
		Vector<String> freeVar = relationVarToString(dlq.getFreeVar());
		for (int i = 0; i < freeVar.size(); i++)
			for (int j = 0; j < relations.size(); j++){	
				if (!relations.elementAt(j).getTable().getKey().contains(freeVar.elementAt(i)))
					return null;
				else if (j == relations.size()-1)
					return dlq.getFreeVar().elementAt(i).getName();
			}
		return null;
	}

/*
 * The second rule of the find safe plan algorithm	
 */
	
	private static String secondRule(DLQuery dlq,  Vector<Relation> relations) {
		Vector<String> dlqFree = relationVarToString(dlq.getFreeVar());
		for (int i = 0; i < dlqFree.size(); i++)
			for (int j = 0; j < relations.size(); j++){
				Relation currRelation = relations.elementAt(j);
				Table currTable = currRelation.getTable();
				if (currTable.getNonKey().contains(dlqFree.elementAt(i))
						&& !checkIntersection(dlq, currTable.getKey()))
					return dlqFree.elementAt(i);
			}
		return null;
	}
	
/*
 * checks Intersection between fields in two table
 */
	
	public static boolean checkIntersection(DLQuery dlq, Vector<String> key) {
		Vector<String> ans = new Vector<String>(); 
		for (int i =  0; i < key.size(); i++)
			ans.add(key.elementAt(i));
		ans.retainAll(dlq.getFreeVar());
		if (ans.size() == 0)			
			return false;
		return true;
	}
}
